// 该模块是让服务器守护进程化的  -- 就是让服务器在后台一直运行
// 不用在用命令了

#if 0 

 1. 什么是守护进程 ???????? 
    // 1. 每个xshell 打开都是有一个会话的 , 这个会话包含 : 默认的进程组 , -- bash 
    // 2. 然后如果要有处理的任务就会创建其它进程组 , 这些进程组都是在一个会话中
    // 3. 守护进程就是要让一个进程启动后要有 *** 独立的会话组 *** 

    // 1. 有独立的会话组 , 所以要创建独立的会话 -- 
    // 2. 不能有控制终端 : 不能和键盘 ,等关联 , 所以必须关闭对应的信号 
    // 3. 不能让自己成为组长 -- 所以必须让子进程执行

#endif

#include <iostream>
#include <string>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "Log.hpp"
#include  "Common.hpp"

using namespace LogModule;

// 传过来两个参数 :
// nochdir -- nochdir == 0 - > 更改工作目录为 "/" , 否则 , 为当前工作目录
// noclose -- noclose == 0 -> 重定向 0 , 1, 2 到 /dev/null , 否则 , 还存在 0,1,2

const std::string devfile = "/dev/null"; // 根目录下的这个文件

void Daemon(int nochdir, int noclose)
{

    // 1. 关闭相关信号 -- 即 : 忽略信号 -- > IO和子进程退出信号等
    // 忽略的是可能导致异常退出的信号 !
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);

    // 2. 不能让自己成为组长 -- 要让子进程创建会话 , 子进程为新会话的组长
    if (fork() > 0)
        exit(0);

    // 走到这里以下就是孙子进程在执行了 , 孤儿进程被系统领养 , 父进程是 : 1

    // 3. 创建单独会话
    setsid();

    // 4. 是否更改当前工作目录到根目录 -- 一般都是更改,增强稳定性
    if (nochdir == 0)
    {
        // 更改目录
        chdir("/");
    }

    // 4. 不能有控制终端 -- 要关闭与控制终端有关的
    //    守护进程，不从键盘输入，也不需要向显示器打印
    //    所以 , 要关闭 0 , 1 ,2 描述符

    // 这里推荐 : 将文件描述符重定向到 -- > /dev/null 下 ,这个里面会自动忽略这些 , 这个就相当于Linux下的垃圾桶
    // 所以要重定向文件描述符 -- 系统调用 -dup2

    // int dup2(int oldfd, int newfd);
    // 是将 : 新的文件描述符拷贝给老的 , 新拷老  , 所以老的指向新的
    // 在这里我们新的是谁呀 ??? 就要打开设备文件 /dev/null

    // noclose 为 0 , 关闭 0 , 1 ,2 描述符 ,即 : 重定向到 -- > /dev/null 下
    if (noclose == 0)
    {
        // 读写方式打开
        int fd = ::open(devfile.c_str(), O_RDWR);
        if (fd < 0)
        {
            LOG(LogLevel::FATAL) << "open " << devfile << " errno";
            exit(OPEN_ERROR);
        }

        //打开成功 -- 重定向 -- dup2 -- 新的拷贝给老的 
        dup2(fd , 0);
        dup2(fd , 1);
        dup2(fd , 2);
        // 将 0 , 1 , 2都指向了fd了 , 所以就被忽略了 , 就关闭了

        close(fd);
    }
}